package Kbd(Kbd(..), RawKbd(..), ScanCode(..), mkKbd) where
import GetPut
import ConfigReg

data ScanCode = ScanCode (Bit 8)
    deriving (Bits, Eq, Literal)

type Kbd = Get ScanCode

interface RawKbd =
    kbclk  :: Bool -> Action
    kbdata :: Bool -> Action
    key  :: Bit 8

--parameter SM_1 = 0;
--parameter SM_2 = 1;
--parameter S_W = 10; // shift register width (DO NOT CHANGE)
--parameter BYTE_ERROR = 8'd0;  // error-code to flag parity-error
--parameter KBCNT = 11; // 11 clocks

mkKbd :: Module (RawKbd, Kbd)
mkKbd =
  module

        -- reg'd versions of inputs, for synchronization to local master clock (clk)
        r_kbclk    :: Reg (Bool) <- mkConfigReg False -- reg'd (synchronized) kbclk
        r_kbdata   :: Reg (Bool) <- mkConfigReg False -- reg'd (synchronized) kbdata
        r_kbclk_1d :: Reg (Bool) <- mkConfigReg False -- 1 clk delayed version of r_kbclk
        kbclk_stb  :: Reg (Bool) <- mkConfigReg False -- pulse on negedge of r_kbclk

        fsm_ps          :: Reg (Bit 1) <- mkConfigReg 0 --SM_1=0
        fsm_lock_kbclkn :: Reg (Bit 1) <- mkConfigReg 0

        -- Component (1) : input-scanner
        s_reg        :: Reg (Bit 10) <- mkConfigReg 0 -- input (kbdata) shift register
        s_reg_err    :: Reg (Bool) <- mkConfigReg False -- final parity result on s_reg (1=error)
        s_reg_parity :: Reg (Bit 1) <- mkConfigReg 0 -- s_reg's running parity
        kbclk_ctr    :: Reg (Bit 4) <- mkConfigReg 0
        kbclk_ctr_eqcnt :: Reg (Bool) <- mkConfigReg False
        debug_reg_obyteload :: Reg (Bool) <- mkConfigReg False
        debug_reg_kbclkctrreset :: Reg (Bool) <- mkConfigReg False

        --Component (2) : output buffer logic
        -- byte data register (last received scancode)
        o_byte :: Reg (Bit 8) <- mkConfigReg 0
        -- byte-status, (1=contain valid data, 0=empty)
        o_byte_rdy :: Reg (Bool) <- mkConfigReg False
        -- o_byte parity error status (1=error, 0=ok)
        o_byte_err :: Reg (Bool) <- mkConfigReg False

        let kbclk_ctr_reset = kbclk_ctr_eqcnt -- glitchless (reg'd signal)
        let kbclk_ctr_eqcnt_ns = ((kbclk_ctr == 10) && (kbclk_stb)) -- next-state

        let fsm_ns = fsm_ps
        let fsm_lock_kbclkn_ns = 1 -- 1=don't lock kbclk, 0=lock kbclk

        let o_byte_load =  (fsm_ps==1) && (not o_byte_rdy)
        let o_kbclk_en = fsm_lock_kbclkn -- 1=enable input, 0=disable input

        let fsm_ns =
             --0 -> -- SM_1 -- READ SCANCODE
             if (fsm_ps == 0) then
                 if ( kbclk_ctr_reset ) then -- kbclk_ctr_reset also clears the latch -> 0
                   1
                  else
                   fsm_ps
              else --1 -> -- SM_2 -- BYTE (scancode) ready for output-buffer (o_byte)
                 if ( o_byte_load ) then -- wait for previous scancode to leave o_byte
                   0
                  else
                   fsm_ps
        let fsm_lock_kbclkn_ns =
             --0 -> -- SM_1 -- READ SCANCODE
             if (fsm_ps == 0) then
                 if ( kbclk_ctr_reset ) then -- kbclk_ctr_reset also clears the latch -> 0
                   0
                  else
                   1
              else --1 -> -- SM_2 -- BYTE (scancode) ready for output-buffer (o_byte)
                 if ( o_byte_load ) then -- wait for previous scancode to leave o_byte
                   1
                  else
                   1


        rules
          {-# ASSERT fire when enabled #-}
          {-# ASSERT no implicit conditions #-}
          "RegIns":
            when True ==> action
                              debug_reg_obyteload := o_byte_load
                              debug_reg_kbclkctrreset := kbclk_ctr_reset
                              -- kbclk_stb - negedge of r_kbclk
                              kbclk_stb  := (not r_kbclk) && (r_kbclk_1d)
                              r_kbclk_1d := r_kbclk
                              --r_kbclk    := i_kbclk
                              --r_kbdata   := i_kbdata
                              fsm_ps     := fsm_ns
                              fsm_lock_kbclkn := fsm_lock_kbclkn_ns
                              if ( kbclk_stb ) then
                                --shift data in from MSB, shift right
                                s_reg := (pack r_kbdata) ++ s_reg[9:1]
                               else
                                noAction
                              kbclk_ctr_eqcnt := kbclk_ctr_eqcnt_ns
                              if ( kbclk_ctr_reset ) then
                                action
                                  kbclk_ctr := 0
                                  s_reg_parity := 0
                                  s_reg_err := (s_reg_parity == 1)
                               else if ( kbclk_stb ) then
                                      action
                                       kbclk_ctr := kbclk_ctr + 1
                                       s_reg_parity := s_reg_parity ^ (pack r_kbdata)
                                     else
                                       noAction
                              if ( o_byte_load ) then  -- transfer data from s_reg?
                               action
                                 o_byte := if s_reg_err then 0 else s_reg[7:0]
                                 o_byte_rdy := True  -- raise rdy line, got new data!
                                 o_byte_err := s_reg_err -- load parity status: 1=error, 0=no error
                               else
                                 if ( o_byte_err ) then
                                  action
                                    o_byte_rdy := False
                                    o_byte_err := False
                                  else
                                    noAction

        return $
         (interface RawKbd
            kbclk i_kbclk =
                          r_kbclk    := i_kbclk
            kbdata i_kbdata =
                          r_kbdata   := i_kbdata
            key = o_byte
          ,
          interface Get
            get = do
                     o_byte_rdy := False --lower rdy line, output buffer is now empty
--                     o_byte_err := False -- clear error-flag on ack
                     return (ScanCode o_byte)
--                when o_byte_rdy
                when o_byte_rdy && (not o_byte_err)
          )


-- This is the old version of mkKbd that uses the Verilog kbscan block
mkKbdVerilog_kbscan :: Module (RawKbd, Kbd)
mkKbdVerilog_kbscan = do
    vkbd :: VKbd <- mkVKbd
    addRules $
        rules
         when vkbd.err == 1
          ==> fromPrimAction vkbd.ack
    return $
        (interface RawKbd
            kbclk x = if x then fromPrimAction vkbd.clk else noAction
            kbdata x = if x then fromPrimAction vkbd.dta else noAction
            key = vkbd.key
        ,interface Get
            get = do
                    fromPrimAction vkbd.ack
                    return (ScanCode vkbd.key)
                when vkbd.rdy == 1 && vkbd.err == 0
        )

interface VKbd =
    key :: Bit 8
    rdy :: Bit 1
    err :: Bit 1
    ack :: PrimAction
    clk :: PrimAction
    dta :: PrimAction


mkVKbd :: Module VKbd
mkVKbd =
    module verilog "kbscan" "clk" "rstn" {
        key = "o_byte"{reg};
        rdy = "o_byte_rdy"{reg};
        err = "o_byte_err"{reg};
        ack = "i_byte_ack";
        clk = "i_kbclk"{reg};
        dta = "i_kbdata"{reg};
    } [ [key,rdy,err,ack,clk,dta] <> [key,rdy,err,ack,clk,dta] ]
